/**
 * @Copyright 			(c) 2019,mculover666 All rights reserved	
 * @filename  			lcd_spi_drv.h
 * @breif				Drive ST7789 LCD based on spi interface
 * @changelog
 *            			v1.0    finish basic function               mculover666    2019/7/10
 *                      v2.0    add macro define to control build   mculover666    2019/7/13
 *                      v2.1    add support for scroll function     mculover666    2021/5/18
 *                      v2.2    optimizing code style               mculover666    2021/5/19
 *                      v2.3    optimizing speed                    mculover666    2021/8/29
 */

#include "lcd_spi2_drv.h"

volatile uint8_t wait_flag = 0;
static lcd_color_params_t s_lcd_color_params = {
    .background_color = BLACK,
    .foreground_color = WHITE
};

static void lcd_hard_reset(void)
{
    LCD_RST(0);
    HAL_Delay(10);
    LCD_RST(1);
		lcd_display_off();
}

static void spi_write_bytes(uint8_t *data, uint16_t size)
{
	while(wait_flag);
	wait_flag = 1 ;
	HAL_SPI_Transmit_DMA(&hspi2,data,size);
}

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(hspi->Instance == hspi2.Instance)
		wait_flag = 0 ;
}

static void lcd_write_cmd(uint8_t cmd)
{
    LCD_WR_RS(0);
    spi_write_bytes(&cmd, 1);
}

static void lcd_write_data(uint8_t dat)
{
    LCD_WR_RS(1);
    spi_write_bytes(&dat, 1);
}

static void lcd_write_color(const uint16_t color)
{
    uint8_t data[2] = {0};

    data[0] = color >> 8;
    data[1] = color;

    LCD_WR_RS(1);
    spi_write_bytes(data, 2);
}

uint16_t rgb2hex_565(uint16_t r, uint16_t g, uint16_t b)
{
    uint16_t color;
    
    r = (r & 0x1F) << 11;
    g = (g & 0x3F) << 5;
    b = b & 0x1F;
    color = r | g | b;

    return color;
}

void lcd_address_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    lcd_write_cmd(0x2a);
    lcd_write_data(x1 >> 8);
    lcd_write_data(x1);
    lcd_write_data(x2 >> 8);
    lcd_write_data(x2);

    lcd_write_cmd(0x2b);
    lcd_write_data(y1 >> 8);
    lcd_write_data(y1);
    lcd_write_data(y2 >> 8);
    lcd_write_data(y2);

    lcd_write_cmd(0x2C);
}

void lcd_display_on(void)
{
    LCD_PWR(1);
}

void lcd_display_off(void)
{
    LCD_PWR(0);
}

void lcd_color_set(uint16_t back_color, uint16_t fore_color)
{
    s_lcd_color_params.background_color = back_color;
    s_lcd_color_params.foreground_color = fore_color;
}

void lcd_clear(void)
{
    uint32_t size, i;
    uint16_t color = s_lcd_color_params.background_color;

    size = LCD_WIDTH * LCD_HEIGHT;
    
    lcd_address_set(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);
    for (i = 0; i < size; i++) {
        lcd_write_color(color);
    }
}

void lcd_init(void)
{
    /* GPIO initialization code in main.c */

    /* SPI initialization code in main.c */

    /* LCD Hard Reset */
    lcd_hard_reset();
    HAL_Delay(120);
	
    /* Sleep Out */
    lcd_write_cmd(0x11);

    /* wait for power stability */
    HAL_Delay(120);

    /* Memory Data Access Control */
    lcd_write_cmd(0x36);
    lcd_write_data(0x00);

    /* RGB 5-6-5-bit  */
    lcd_write_cmd(0x3A);
    lcd_write_data(0x65);

    /* Porch Setting */
    lcd_write_cmd(0xB2);
    lcd_write_data(0x0C);
    lcd_write_data(0x0C);
    lcd_write_data(0x00);
    lcd_write_data(0x33);
    lcd_write_data(0x33);

    /*  Gate Control */
    lcd_write_cmd(0xB7);
    lcd_write_data(0x72);

    /* VCOM Setting */
    lcd_write_cmd(0xBB);
    lcd_write_data(0x3D);   //Vcom=1.625V

    /* LCM Control */
    lcd_write_cmd(0xC0);
    lcd_write_data(0x2C);

    /* VDV and VRH Command Enable */
    lcd_write_cmd(0xC2);
    lcd_write_data(0x01);

    /* VRH Set */
    lcd_write_cmd(0xC3);
    lcd_write_data(0x19);

    /* VDV Set */
    lcd_write_cmd(0xC4);
    lcd_write_data(0x20);

    /* Frame Rate Control in Normal Mode */
    lcd_write_cmd(0xC6);
    //lcd_write_data(0x0F);	//60MHZ
		lcd_write_data(0x01);		//111Hz

    /* Power Control 1 */
    lcd_write_cmd(0xD0);
    lcd_write_data(0xA4);
    lcd_write_data(0xA1);

    /* Positive Voltage Gamma Control */
    lcd_write_cmd(0xE0);
    lcd_write_data(0xD0);
    lcd_write_data(0x04);
    lcd_write_data(0x0D);
    lcd_write_data(0x11);
    lcd_write_data(0x13);
    lcd_write_data(0x2B);
    lcd_write_data(0x3F);
    lcd_write_data(0x54);
    lcd_write_data(0x4C);
    lcd_write_data(0x18);
    lcd_write_data(0x0D);
    lcd_write_data(0x0B);
    lcd_write_data(0x1F);
    lcd_write_data(0x23);

    /* Negative Voltage Gamma Control */
    lcd_write_cmd(0xE1);
    lcd_write_data(0xD0);
    lcd_write_data(0x04);
    lcd_write_data(0x0C);
    lcd_write_data(0x11);
    lcd_write_data(0x13);
    lcd_write_data(0x2C);
    lcd_write_data(0x3F);
    lcd_write_data(0x44);
    lcd_write_data(0x51);
    lcd_write_data(0x2F);
    lcd_write_data(0x1F);
    lcd_write_data(0x1F);
    lcd_write_data(0x20);
    lcd_write_data(0x23);
    /* Display Inversion On */
    lcd_write_cmd(0x21);
    lcd_write_cmd(0x29);
		lcd_clear();
}

void lcd_draw_point(uint16_t x, uint16_t y,uint16_t color)
{
    lcd_address_set(x, y, x, y);
    lcd_write_color(color);
}

void lcd_fill_with_buffer(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t *color)
{
    uint32_t size, i;

    size = (x2 - x1 + 1) * (y2 - y1 + 1);
    
    lcd_address_set(x1, y1, x2, y2);
    for (i = 0; i < size; i++) {
        lcd_write_color(*color++);
    }
}


